<?php

declare(strict_types=1);

namespace Core\System;

use App\Common\Extend\Guard\Auth as AuthAnnotation;
use Core\HyperfUnit\Callback;
use Hyperf\Di\Annotation\AnnotationCollector;
use Zms\Unit\Arr;
use Zms\Unit\Tree;
use Zms\Unit\Str;

class AuthUnit
{
    const DEFAULT_ACTION_NAME = [
        'index' => '管理,查看',
        'list' => '管理,查看',
        'lists' => '管理,查看',
        'create' => '添加',
        'created' => '添加',
        'update' => '修改',
        'updated' => '修改',
        'remove' => '删除',
        'delete' => '删除',
        'deleted' => '删除'
    ];

    /**
     * 收集菜单权限
     * @param string $app
     * @return array
     */
    public static function collectMenuPermissions(string $app): array
    {
        //全部节点权限
        $allPermissions = self::permissionsFormData($app);
        $permissions = [];
        foreach (self::collectPermissions($app) as $item) {
            if (!$item['view']) continue;//没绑定视图,跳过
            if ($item['tourist'] === true) continue;//游客可访问,跳过
            if (!$item['sign']) continue;//未设置权限值,跳过
            if ($item['sign'] === 'skip') continue;//权限不校验,跳过
            if (count($item['relyon']) > 0) continue;//依赖权限,跳过
            $permissions[$item['view']] = Tree::getFullValue($allPermissions, $item['sign'], 'pid', 'sign');
        }
        return $permissions;
    }

    /**
     * 收集的权限转为表单数据
     * @param string|null $app
     * @param bool $reduce
     * @return array
     */
    public static function permissionsFormData(?string $app, bool $reduce = false): array
    {
        return Callback::cache(md5(__METHOD__ . ($app ?? 'null') . ($reduce ? 1 : 0)), function () use ($app, $reduce) {
            $all = [];
            foreach (self::collectPermissions($app) as $item) {
                if ($item['sign'] === 'skip' || $item['tourist'] === true || count($item['relyon']) > 0) {
                    continue;
                }
                $link = '';
                $level = count($item['name']) - 1;
                foreach ($item['name'] as $index => $row) {
                    $key = Str::md5_16($link . $row);
                    $pid = $link ? Str::md5_16($link) : '';
                    if ($level === $index) {//最后一级
                        $item['key'] = $item['sign'];
                        $item['pid'] = $pid;
                        $item['name'] = $row;
                        if ($reduce) {
                            $item = Arr::remove($item, ['class', 'action', 'server', 'app', 'view', 'describe', 'relyon', 'tourist', 'guard', 'record', 'logger', 'mode', 'check']);
                        }
                        $all[] = $item;
                    } else {//不是最后一级
                        $permissions['key'] = $key;
                        $permissions['pid'] = $pid;
                        $permissions['name'] = $row;
                        $permissions['sign'] = $key;
                        $all[] = $permissions;
                    }
                    $link .= $row;
                }
            }
            return Arr::getTree($all, 'key', 'pid', 'children', '');
        });
    }

    /**
     * 收集权限
     * @param ?string $app
     * @return array
     */
    public static function collectPermissions(?string $app = null): array
    {
        return Callback::cache(md5(__METHOD__ . ($app ?? 'null')), function () use ($app) {
            $authAnnotationName = AuthAnnotation::class;//权限注解名称
            $annotationList = AnnotationCollector::list();//全部注解
            $annotationData = [];//全部权限数据

            foreach ($annotationList as $className => $info) {
                //类上未配置该注解则忽略
                if (empty($info['_c'][$authAnnotationName])) continue;

                /**
                 * @var AuthAnnotation $annotation
                 * @var AuthAnnotation $actionAnnotation
                 */
                $annotation = $info['_c'][$authAnnotationName];//获取类注解
                if ($app && $app !== $annotation->app) continue;//不是要收集的权限,跳过
                if (!$annotation->guard) continue;//未配置认证信息
                if ($annotation->remove) continue;//移除
                $annotation->label = $annotation->label ?? [];//节点名称
                $annotation->mode = $annotation->mode ?? ['*'];//访问权限
                $annotation->tourist = $annotation->tourist ?? false;//游客访问
                $annotation->sign = $annotation->sign ?? '';//游客访问
                $annotation->relyon = $annotation->relyon ?? [];
                $annotation->logger = $annotation->logger ?? true;//是否记录日志
                $annotation->record = $annotation->record ?? [];//需要记录的参数
                $annotation->process = $annotation->process ?? true;//权限前缀
                $annotation->prefix = $annotation->prefix ?? '';//权限前缀
                if (!(isset($info['_m']) && is_array($info['_m']) && count($info['_m']) > 0)) continue;//没控制器，返回
                //获取里面的方法节点
                foreach ($info['_m'] as $actionName => $actionInfo) {
                    if (empty($actionInfo[$authAnnotationName])) continue;//没权限注解，不干了
                    /**
                     * @var AuthAnnotation $actionAnnotation
                     */
                    $actionAnnotation = $actionInfo[$authAnnotationName];
                    if ($actionAnnotation->remove) continue;
                    if (empty($actionAnnotation->label)) {
                        if (isset(self::DEFAULT_ACTION_NAME[$actionName])) {
                            $actionAnnotation->label = [self::DEFAULT_ACTION_NAME[$actionName]];
                        } elseif ($actionAnnotation->describe) {
                            $actionAnnotation->label = [$actionAnnotation->describe];
                        } else {
                            $actionAnnotation->label = [$actionAnnotation->sign];
                        }
                    }
                    $route = RouteUnit::getActionRoute($className, $actionName);
                    //判断权限是否需要拼接权限
                    if ($actionAnnotation->sign && $actionAnnotation->sign !== 'skip') {
                        if (!preg_match_all('/(\.)/', $actionAnnotation->sign)) {
                            $actionAnnotation->sign = $annotation->prefix ? ($annotation->prefix . '.' . $actionAnnotation->sign) : $actionAnnotation->sign;
                        }
                    }
                    $annotationData[] = [
                        'class' => $className,
                        'action' => $actionName,
                        'router' => $route['router'] ?? '',
                        'server' => $route['server'] ?? '',
                        'app' => $actionAnnotation->app ?? $annotation->app,
                        'view' => $actionAnnotation->view ?? '',
                        'process' => $actionAnnotation->process ?? $annotation->process,
                        'name' => array_merge($annotation->label, $actionAnnotation->label),
                        'sign' => $actionAnnotation->sign ?? $annotation->sign,
                        'check' => $actionAnnotation->check ?? true,
                        'describe' => $actionAnnotation->describe,
                        'relyon' => $actionAnnotation->relyon ?? $annotation->relyon,
                        'tourist' => $actionAnnotation->tourist ?? $annotation->tourist,//游客模式，未设置就继承类注解
                        'mode' => $actionAnnotation->mode ?? $annotation->mode,//开发模式，未设置就继承类注解
                        'guard' => $actionAnnotation->guard ?? $annotation->guard,//用户配置
                        'logger' => $actionAnnotation->logger ?? $annotation->logger,//日志配置
                        'record' => $actionAnnotation->record ?? $annotation->record,//需要记录的参数
                    ];
                }
            }
            return $annotationData;
        });
    }

    /**
     * 获取全部权限节点
     */
    public static function permissionValueAll(?string $app): array
    {
        return Callback::cache(md5(__METHOD__ . ($app ?? 'null')), function () use ($app) {
            $all = self::collectPermissions($app);
            $permission = Arr::getValues($all, 'sign');
            $permission = array_unique($permission);
            $value = [];
            foreach ($permission as $sign) {
                if ($sign && $sign !== 'skip') {
                    $value[] = $sign;
                }
            }
            return $value;
        });
    }


    /**
     * 获取节点权限信息
     * @param string $class
     * @param string $action
     * @return ?array
     */
    public static function getActionPermissions(string $class, string $action): ?array
    {
        return Callback::cache(md5(__METHOD__ . $class . $action), function () use ($class, $action) {
            foreach (self::collectPermissions() as $item) {
                if ($item['class'] === $class && $item['action'] === $action) {
                    return $item;
                }
            }
            return null;
        });
    }
}